using System;
using System.Runtime.InteropServices;
using System.Security;
using WindowsAPI.Service;

namespace WindowsAPI
{
    [SuppressUnmanagedCodeSecurity, Dll(FileName)]
    public static class AdvAPI32
    {
        /* ------------------------------------------------------------------------------------------ */

        public const string FileName = "advapi32.dll";

        public const int SE_PRIVILEGE_ENABLED = 0x00000002;


        /* ------------------------------------------------------------------------------------------ */


        [DllImport(FileName, SetLastError = true)]
        public static extern bool GetSecurityDescriptorDacl(
                    IntPtr pSD,
                    out bool daclPresent,
                    out IntPtr pDacl,
                    out bool daclDefaulted);

        [DllImport(FileName, SetLastError = true)]
        public static extern bool AbortSystemShutdown(string lpMachineName = null);

        [DllImport(FileName)]
        public static extern bool InitiateSystemShutdownEx(
                    [MarshalAs(UnmanagedType.LPStr)]string lpMachineName = null,
                    [MarshalAs(UnmanagedType.LPStr)]string lpMessage = null,
                    int dwTimeout = 0,
                    bool bForceAppsClosed = false,
                    bool bRebootAfterShutdown = false,
                    ShutdownReason dwReason = ShutdownReason.MajorOther);

        [DllImport(FileName, SetLastError = true)]
        public static extern bool InitiateSystemShutdown(
                    [MarshalAs(UnmanagedType.LPStr)] string lpMachinename = null,
                    [MarshalAs(UnmanagedType.LPStr)] string lpMessage = null,
                    int dwTimeout = 0,
                    bool bForceAppsClosed = false,
                    bool bRebootAfterShutdown = false);

        // Use this signature if you want the previous state information returned

        [Link("http://www.rsdn.ru/article/baseserv/privileges.xml")]
        [DllImport(FileName, ExactSpelling = true, SetLastError = true)]
        public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle,
                                                        [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
                                                        ref TOKEN_PRIVILEGES NewState,
                                                        uint BufferLengthInBytes,
                                                        ref TOKEN_PRIVILEGES PreviousState,
                                                        out uint ReturnLengthInBytes);

        [DllImport(FileName, ExactSpelling = true, SetLastError = true)]
        public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle,
                                                        [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
                                                        ref TOKEN_PRIVILEGES NewState,
                                                        int BufferLengthInBytes,
                                                        IntPtr pPreviousState,
                                                        IntPtr pReturnLengthInBytes);

        [DllImport(FileName, SetLastError = true)]
        public static extern bool PrivilegeCheck([In]IntPtr ClientToken, [Out]PRIVILEGE_SET RequiredPrivileges, out bool pfResult);

        [DllImport(FileName, ExactSpelling = true, SetLastError = true)]
        public static extern bool OpenProcessToken(IntPtr ProcessHandle, DesiredAccessType DesiredAccess, out IntPtr TokenHandle);

        [DllImport(FileName, SetLastError = true)]
        public static extern bool GetTokenInformation(
                    IntPtr TokenHandle,
                    TOKEN_INFORMATION_CLASS TokenInformationClass,
                    IntPtr TokenInformation,
                    uint TokenInformationLength,
                    out uint ReturnLength);

        [DllImport(FileName, ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);

        /* ------------------------------------------------------------------------------------------ */

        public static byte[] GetSIDByteArr(IntPtr processHandle)
        {
            const int MAX_INTPTR_BYTE_ARR_SIZE = 512;
            IntPtr tokenHandle;
            var sidBytes = new byte[0];

            // Get the Process Token
            if(!OpenProcessToken(processHandle, DesiredAccessType.TOKEN_READ, out tokenHandle))
                Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
            //throw new ApplicationException("Could not get process token.  Win32 Error Code: " + Marshal.GetLastWin32Error());

            uint tokenInfoLength = 0;
            GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenUser,
                                IntPtr.Zero, tokenInfoLength, out tokenInfoLength);
            var tokenInfo = Marshal.AllocHGlobal((int)tokenInfoLength);
            var result = GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenUser, tokenInfo, tokenInfoLength, out tokenInfoLength);

            // Get the User SID
            if(result)
            {
                var tokenUser = (TOKEN_USER)Marshal.PtrToStructure(tokenInfo, typeof(TOKEN_USER));
                sidBytes = new byte[MAX_INTPTR_BYTE_ARR_SIZE];  // Since I don't yet know how to be more precise w/ the size of the byte arr, it is being set to 512
                Marshal.Copy(tokenUser.User.Sid, sidBytes, 0, MAX_INTPTR_BYTE_ARR_SIZE);  // get a byte[] representation of the SID
            }
            else
                Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
            //throw new ApplicationException("Could not get process token.  Win32 Error Code: " + Marshal.GetLastWin32Error());

            return sidBytes;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct PRIVILEGE_SET
    {
        public uint PrivilegeCount;
        public uint Control;
        [MarshalAs(UnmanagedType.LPArray)]
        public LUID_AND_ATTRIBUTES[] Privilege;
    }

    //public struct PRIVILEGE_SET
    //{
    //    public uint PrivilegeCount;
    //    public uint Control;
    //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] // 2 privilege structs flatten to be 6 int's !!!!
    //    public int[] Privilege_1;
    //}

    [StructLayout(LayoutKind.Sequential)]
    public struct LUID_AND_ATTRIBUTES
    {
        private LUID Luid;
        private uint Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct LocalIdAndAttribute
    {
        public long Luid;
        public int Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TOKEN_USER { public SID_AND_ATTRIBUTES User; }

    [StructLayout(LayoutKind.Sequential)]
    public struct SID_AND_ATTRIBUTES
    {

        public IntPtr Sid;
        public int Attributes;
    }

    public enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin
    }

    [Flags]
    public enum DesiredAccessType : uint
    {
        STANDARD_RIGHTS_REQUIRED = 0x000F0000,
        STANDARD_RIGHTS_READ = 0x00020000,
        TOKEN_ASSIGN_PRIMARY = 0x0001,
        TOKEN_DUPLICATE = 0x0002,
        TOKEN_IMPERSONATE = 0x0004,
        TOKEN_QUERY = 0x0008,
        TOKEN_QUERY_SOURCE = 0x0010,
        TOKEN_ADJUST_PRIVILEGES = 0x0020,
        TOKEN_ADJUST_GROUPS = 0x0040,
        TOKEN_ADJUST_DEFAULT = 0x0080,
        TOKEN_ADJUST_SESSIONID = 0x0100,
        TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY,
        TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE
                            | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS
                            | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID

    }

    [UsedIn(typeof(AdvAPI32), Aditional = "LookupPrivilegeValue")]
    public static class SE_Signatures
    {
        public const string SE_ASSIGNPRIMARYTOKEN_NAME = "SeAssignPrimaryTokenPrivilege";
        public const string SE_AUDIT_NAME = "SeAuditPrivilege";
        public const string SE_BACKUP_NAME = "SeBackupPrivilege";
        public const string SE_CHANGE_NOTIFY_NAME = "SeChangeNotifyPrivilege";
        public const string SE_CREATE_GLOBAL_NAME = "SeCreateGlobalPrivilege";
        public const string SE_CREATE_PAGEFILE_NAME = "SeCreatePagefilePrivilege";
        public const string SE_CREATE_PERMANENT_NAME = "SeCreatePermanentPrivilege";
        public const string SE_CREATE_SYMBOLIC_LINK_NAME = "SeCreateSymbolicLinkPrivilege";
        public const string SE_CREATE_TOKEN_NAME = "SeCreateTokenPrivilege";
        public const string SE_DEBUG_NAME = "SeDebugPrivilege";
        public const string SE_ENABLE_DELEGATION_NAME = "SeEnableDelegationPrivilege";
        public const string SE_IMPERSONATE_NAME = "SeImpersonatePrivilege";
        public const string SE_INC_BASE_PRIORITY_NAME = "SeIncreaseBasePriorityPrivilege";
        public const string SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege";
        public const string SE_INC_WORKING_SET_NAME = "SeIncreaseWorkingSetPrivilege";
        public const string SE_LOAD_DRIVER_NAME = "SeLoadDriverPrivilege";
        public const string SE_LOCK_MEMORY_NAME = "SeLockMemoryPrivilege";
        public const string SE_MACHINE_ACCOUNT_NAME = "SeMachineAccountPrivilege";
        public const string SE_MANAGE_VOLUME_NAME = "SeManageVolumePrivilege";
        public const string SE_PROF_SINGLE_PROCESS_NAME = "SeProfileSingleProcessPrivilege";
        public const string SE_RELABEL_NAME = "SeRelabelPrivilege";
        public const string SE_REMOTE_SHUTDOWN_NAME = "SeRemoteShutdownPrivilege";
        public const string SE_RESTORE_NAME = "SeRestorePrivilege";
        public const string SE_SECURITY_NAME = "SeSecurityPrivilege";
        public const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
        public const string SE_SYNC_AGENT_NAME = "SeSyncAgentPrivilege";
        public const string SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege";
        public const string SE_SYSTEM_PROFILE_NAME = "SeSystemProfilePrivilege";
        public const string SE_SYSTEMTIME_NAME = "SeSystemtimePrivilege";
        public const string SE_TAKE_OWNERSHIP_NAME = "SeTakeOwnershipPrivilege";
        public const string SE_TCB_NAME = "SeTcbPrivilege";
        public const string SE_TIME_ZONE_NAME = "SeTimeZonePrivilege";
        public const string SE_TRUSTED_CREDMAN_ACCESS_NAME = "SeTrustedCredManAccessPrivilege";
        public const string SE_UNDOCK_NAME = "SeUndockPrivilege";
        public const string SE_UNSOLICITED_INPUT_NAME = "SeUnsolicitedInputPrivilege";
    }
}